﻿
#include "stdafx.h"
#include "colordef.h"
#include "alpha_x86.inl"

//////////////////////////////////////////////////////////////////////////

VOID CCALL AlphaBlendStandard(WORD src, LPVOID dest, UINT pitch, UINT cx, UINT cy, BYTE alpha)
{
	LPBYTE line = static_cast<LPBYTE>(dest);

	for (; cy; cy--, line += pitch)
	{
		LPWORD img = reinterpret_cast<LPWORD>(line);

		for (UINT i = cx; i; i--, img++)
			AlphaBlendStandard(&src, img, &alpha);
	}
}

VOID CCALL AlphaBlendStandard(LPCVOID src, UINT src_pitch, LPVOID dest, UINT dest_pitch, LPCVOID alpha, UINT alpha_pitch, UINT cx, UINT cy)
{
	LPCBYTE src_ln = static_cast<LPCBYTE>(src);
	LPBYTE dest_ln = static_cast<LPBYTE>(dest);
	LPCBYTE alpha_ln = static_cast<LPCBYTE>(alpha);

	for (; cy; cy--, src_ln += src_pitch, dest_ln += dest_pitch, alpha_ln += alpha_pitch)
	{
		LPCWORD src_ptr = reinterpret_cast<LPCWORD>(src_ln);
		LPWORD dest_ptr = reinterpret_cast<LPWORD>(dest_ln);
		LPCBYTE alpha_ptr = reinterpret_cast<LPCBYTE>(alpha_ln);

		for (UINT i = cx; i; i--, src_ptr++, dest_ptr++, alpha_ptr++)
			AlphaBlendStandard(src_ptr, dest_ptr, alpha_ptr);
	}
}

VOID CCALL AlphaBlendFullLevel(WORD src, LPVOID dest, UINT pitch, UINT cx, UINT cy, BYTE alpha)
{
	LPBYTE line = static_cast<LPBYTE>(dest);

	for (; cy; cy--, line += pitch)
	{
		LPWORD img = reinterpret_cast<LPWORD>(line);

		for (UINT i = cx; i; i--, img++)
			AlphaBlendFullLevel(&src, img, &alpha);
	}
}

VOID CCALL AlphaBlendFullLevel(LPCVOID src, UINT src_pitch, LPVOID dest, UINT dest_pitch, LPCVOID alpha, UINT alpha_pitch, UINT cx, UINT cy)
{
	LPCBYTE src_ln = static_cast<LPCBYTE>(src);
	LPBYTE dest_ln = static_cast<LPBYTE>(dest);
	LPCBYTE alpha_ln = static_cast<LPCBYTE>(alpha);

	for (; cy; cy--, src_ln += src_pitch, dest_ln += dest_pitch, alpha_ln += alpha_pitch)
	{
		LPCWORD src_ptr = reinterpret_cast<LPCWORD>(src_ln);
		LPWORD dest_ptr = reinterpret_cast<LPWORD>(dest_ln);
		LPCBYTE alpha_ptr = alpha_ln;

		for (UINT i = cx; i; i--, src_ptr++, dest_ptr++, alpha_ptr++)
#if 1
		{
			struct ENHCOLOR
			{
				WORD b:5;
				WORD g:6;
				WORD r:5;
			};

			INT r, g, b;

			if (!*alpha_ptr)
				continue;

			if (*alpha_ptr == COLOR_AMAX)
			{
				*dest_ptr = *src_ptr;
				continue;
			}

			const ENHCOLOR &src_s = reinterpret_cast<const ENHCOLOR &>(*src_ptr);
			ENHCOLOR &dest_s = reinterpret_cast<ENHCOLOR &>(*dest_ptr);

			r = (src_s.r * (*alpha_ptr) + dest_s.r * (255 - (*alpha_ptr))) >> 8;
			g = (src_s.g * (*alpha_ptr) + dest_s.g * (255 - (*alpha_ptr))) >> 8;
			b = (src_s.b * (*alpha_ptr) + dest_s.b * (255 - (*alpha_ptr))) >> 8;

			*dest_ptr = (r << 11) | (g << 5) | b;
		}
#else
			AlphaBlendFullLevel(src_ptr, dest_ptr, alpha_ptr);
#endif
	}
}

VOID CCALL AlphaBlend32LevelX86(WORD src, LPVOID dest, UINT pitch, UINT cx, UINT cy, BYTE alpha)
{
	LPBYTE line = static_cast<LPBYTE>(dest);

	for (; cy; cy--, line += pitch)
	{
		LPWORD img = reinterpret_cast<LPWORD>(line);

		for (UINT i = cx; i; i--, img++)
			AlphaBlend32LevelX86(&src, img, &alpha);
	}
}

VOID CCALL AlphaBlend32LevelX86(LPCVOID src, UINT src_pitch, LPVOID dest, UINT dest_pitch, LPCVOID alpha, UINT alpha_pitch, UINT width, UINT height)
{
#if 1
	// 公式 dest = (src - dest) * (alpha / 8) / 32 + dest
	// [M] 存储器访问	应尽量减少内存访问次数(不过因为有Cache，在数据量小的时候效果不明显)
	// [L] 长运算操作	即乘除运算，CPU时钟占用率较高的指令
	// [W] 16位操作		动态执行(P6系列)处理器中对同一寄存器连续切换16/32位操作会存在延时，应尽可能避免
	// [B] 8位操作		尽可能的先清零32位寄存器再载入，同时尽量不改变高24位，否则会有延时
	// [J] 条件分支	针对CPU分支预测功能可以进行优化
	// [0] 无循环		最外层，低频率执行代码
	// [1] 行循环		第一层循环，中频率执行代码
	// [2] 像素循环	最内层循环，高频率执行代码
	__asm
	{
		// 把常用指针放进寄存器
		mov		esi, src			// [0][M]
		mov		edi, dest			// [0][M]
		mov		eax, alpha			// [0][M]

line_loop:
		mov		ecx, height			// [1][M]
		test	ecx, ecx			// [1]
		jz		quit				// [1][J]
		push	esi					// [1][M]
		push	edi					// [1][M]
		push	eax					// [1][M]
		mov		ecx, width			// [1][M]

pixel_loop:
		jecxz	end_line			// [2][J]
		xor		edx, edx			// [2]
		mov		dl, [eax]			// [2][M][B] edx = alpha
		test	edx, edx			// [2]
		jz		pixel_end			// [2][J]
		add		edx, 4				// [2] ALPHA误差补正
		shr		edx, 3				// [2] 只保留32级（2的5次方）ALPHA
		push	eax					// [2][M]
		push	ecx					// [2][M]
		push	edx					// [2][M]

		// src/dest变形（为减少动态执行CPU的寄存器阻塞以及提高流水线效率而穿插进行）
		// 变形方式：
		//               rgb                               .g.r.b
		//     [RRRRR][GGGGGG][BBBBB] -> [00000][GGGGGG][00000][RRRRR][000000][BBBBB]
		movzx	edx, word ptr [esi]	// [2][M][W]
		movzx	ecx, word ptr [edi]	// [2][M][W]
		mov		eax, edx			// [2] ...rgb
		mov		ebx, ecx			// [2] ...rgb
		shl		eax, 16				// [2] rgb...
		shl		ebx, 16				// [2] rgb...
		or		eax, edx			// [2] rgbrgb
		or		ebx, ecx			// [2] rgbrgb
		and		eax, 07e0f81fh		// [2] .g.r.b
		and		ebx, 07e0f81fh		// [2] .g.r.b

		// 混色
		pop		edx					// [2][M]
		sub		eax, ebx			// [2] eax = src - dest
		mul		edx					// [2][L] gxrxbx
		shr		eax, 5				// [2] .gxrxb
		add		eax, ebx			// [2] + dest
		and		eax, 07e0f81fh		// [2] .g.r.b

		// 还原
		mov		ebx, eax			// [2]
		shr		ebx, 16				// [2] ....g.
		or		eax, ebx			// [2] xxxrgb

		// 保存
		mov		[edi], ax			// [2][M][W]
		pop		ecx					// [2][M]
		pop		eax					// [2][M]

pixel_end:
		dec		ecx					// [2]
		add		esi, 2				// [2]
		add		edi, 2				// [2]
		inc		eax					// [2]
		jmp		pixel_loop			// [2]

end_line:
		pop		eax					// [1][M]
		pop		edi					// [1][M]
		pop		esi					// [1][M]
		dec		height				// [1][M]
		add		eax, alpha_pitch	// [1][M]
		add		edi, dest_pitch		// [1][M]
		add		esi, src_pitch		// [1][M]
		jmp		line_loop			// [1]

quit:
	}
#else
	LPCBYTE src_ln = static_cast<LPCBYTE>(src);
	LPBYTE dest_ln = static_cast<LPBYTE>(dest);
	LPCBYTE alpha_ln = static_cast<LPCBYTE>(alpha);

	for (; height; height--, src_ln += src_pitch, dest_ln += dest_pitch, alpha_ln += alpha_pitch)
	{
		LPCWORD src_ptr = reinterpret_cast<LPCWORD>(src_ln);
		LPWORD dest_ptr = reinterpret_cast<LPWORD>(dest_ln);
		LPCBYTE alpha_ptr = alpha_ln;

		for (UINT i = width; i; i--, src_ptr++, dest_ptr++, alpha_ptr++)
			AlphaBlend32LevelX86(src_ptr, dest_ptr, alpha_ptr);
	}
#endif
}

//////////////////////////////////////////////////////////////////////////
